package robocup.connection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import robocup.component.RobocupAgent;

/**
 * This class is used as communication interface to SoccerScope. In order to
 * avoid hard dependencies to soccerscope, this has been realized using java
 * reflection (no import instructions are neccessary) and the mediator and
 * singleton patterns. This allows to detach soccerscope easiliy from the
 * framework. <br>
 * <br>
 * Note: Do not make any changes unless you know what you do here!
 */
public class TeamSoccerScopeMediator {

  /**
   * Reference to the soccerscope object
   */
  private static Object soccerScopeInstance              = null;

  /**
   * A reflected method for adding heared messages to soccerscope.
   */
  private static Method soccerScopeAddHearMessageToScene = null;

  /**
   * A reflected method for adding said messages to soccerscope.
   */
  private static Method soccerScopeAddSayMessageToScene  = null;

  /**
   * A reflected class for the registration of an agent to soccerscope.
   * <code>Register.java</code>
   */
  private static final Class  registerClass                    = TeamSoccerScopeMediator
                                                             .getRegisterClass();

  /**
   * A reflected method for adding state-assessments to soccerscope.
   */
  private static final Method registerAddStateAss3             = TeamSoccerScopeMediator
                                                             .getRegisterAddStateAss3();

  /**
   * A reflected method for register an agent to soccerscope. Used with
   * <code>registerClass</code>.
   */
  private static final Method registerPlayerBean               = TeamSoccerScopeMediator
                                                             .getRegisterPlayerBean();

  /**
   * A reflected method for adding received infos to soccerscope.
   */
  private static final Method registerInfoReceived             = TeamSoccerScopeMediator
                                                             .getRegisterInfoReceived();

  /**
   * A reflected method for adding a hole to soccerscope.
   */
  private static final Method registerHole                     = TeamSoccerScopeMediator
                                                             .getRegisterHole();

  /**
   * This is the unidirectional interface from the robocup framework to
   * soccerscope. Sending all data to soccerscope (world-model data of the
   * agents) is done by calling some methods via reflection.
   */
  private TeamSoccerScopeMediator() {

  }

  /**
   * This method creates a method by reflection, which is later used to add send
   * messages (via say) to a scene of soccerscope.
   * 
   * @return the corresponding method
   */
  private static Method getSoccerScopeAddSayMessageToScene() {

    Method m = null;
    try {
      m = TeamSoccerScopeMediator.soccerScopeInstance.getClass().getMethod(
          "addSayMessageToScene", Integer.TYPE, String.class);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchMethodException e) {
      e.printStackTrace();
    }
    return m;
  }

  /**
   * This method creates a method by reflection, which is later used to add
   * heared messages to a scene of soccerscope.
   * 
   * @return the corresponding method
   */
  private static Method getSoccerScopeAddHearMessageToScene() {

    Method m = null;
    try {
      m = TeamSoccerScopeMediator.soccerScopeInstance.getClass().getMethod(
          "addHearMessageToScene", Integer.TYPE, String.class);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchMethodException e) {
      e.printStackTrace();
    }
    return m;
  }

  /**
   * This method creates a method by reflection, which is later used to add
   * holes of an agent to a scene of soccerscope.
   * 
   * @return the corresponding method
   */
  private static Method getRegisterHole() {

    Method m = null;
    try {
      m = TeamSoccerScopeMediator.registerClass.getMethod("hole",
          RobocupAgent.class, Integer.TYPE);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchMethodException e) {
      e.printStackTrace();
    }
    return m;
  }

  /**
   * This method creates a method by reflection, which is later used to add a
   * received info to a scene of soccerscope.
   * 
   * @return the corresponding method
   */
  private static Method getRegisterInfoReceived() {

    Method m = null;
    try {
      m = TeamSoccerScopeMediator.registerClass.getMethod("infoReceived",
          RobocupAgent.class);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchMethodException e) {
      e.printStackTrace();
    }
    return m;
  }

  /**
   * This method creates a method by reflection, which is later used to register
   * an agent at soccerscope, which is a precondition for adding world-model
   * data later on.
   * 
   * @return the corresponding method
   */
  private static Method getRegisterPlayerBean() {

    Method m = null;
    try {
      m = TeamSoccerScopeMediator.registerClass.getMethod("registerPlayerBean",
          RobocupAgent.class);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchMethodException e) {
      e.printStackTrace();
    }
    return m;
  }

  /**
   * This method creates a method by reflection, which is later used to add a
   * state-assessment to a scene of soccerscope.
   * 
   * @return the corresponding method
   */
  private static Method getRegisterAddStateAss3() {

    Method m = null;
    try {

      m = TeamSoccerScopeMediator.registerClass.getMethod("addStateAss",
          String.class, RobocupAgent.class);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchMethodException e) {
      e.printStackTrace();
    }
    return m;
  }

  /**
   * This method creates a reflected class of the type <code>
   * soccerscope.daistatistic.Register</code>.
   * This is used to register agents to soccerscope.
   * 
   * @return the register class
   */
  private static Class getRegisterClass() {

    Class c = null;
    try {
      c = Class.forName("soccerscope.daistatistic.Register");
    }
    catch (final ClassNotFoundException e) {
      e.printStackTrace();
    }
    return c;
  }

  /**
   * This method returns the complete SoccerScope instance.
   * 
   * @return the monitor main class
   */
  private static Object getSoccerScopeInstance() {

    Object o = null;

    Class c;
    try {
      c = Class.forName("soccerscope.SoccerScope");
      final Method m = c.getMethod("getInstance", (Class[]) null);
      o = m.invoke(c, (Object[]) null);
    }
    catch (final ClassNotFoundException e) {
      e.printStackTrace();
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchMethodException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
    catch (final InvocationTargetException e) {
      e.printStackTrace();
    }

    return o;
  }

  /**
   * Add state assessements to history. Used by the agents.
   * 
   * @param s
   *          the StateInfo
   * @param wm
   *          the worldmodel of the player agent
   */
  public static void registerAddStateAss(final String s,
      final RobocupAgent agent) {

    try {

      TeamSoccerScopeMediator.registerAddStateAss3.invoke(
          TeamSoccerScopeMediator.registerClass, s, agent);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
    catch (final InvocationTargetException e) {
      e.printStackTrace();
    }
  }

  /**
   * Method to register an agent at soccerscope. Used therefore by the agents
   * 
   * @param ra -
   *          the robocup-agent.
   */
  public static void registerPlayerBean(final RobocupAgent ra) {

    try {
      TeamSoccerScopeMediator.registerPlayerBean.invoke(
          TeamSoccerScopeMediator.registerClass, ra);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
    catch (final InvocationTargetException e) {
      e.printStackTrace();
    }
  }

  /**
   * Method for adding received infos to soccerscope. This is used by the
   * agents.
   * 
   * @param ra -
   *          the robocup-agent
   */
  public static void registerInfoReceived(final RobocupAgent ra) {

    try {
      TeamSoccerScopeMediator.registerInfoReceived.invoke(
          TeamSoccerScopeMediator.registerClass, ra);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
    catch (final InvocationTargetException e) {
      e.printStackTrace();
    }
  }

  /**
   * Method to register a hole for an agent at soccerscope. Used by the agent.
   * 
   * @param ra -
   *          the robocup-agent
   * @param numberOfNewHoles -
   *          the amount of holes to register
   */
  public static void registerHole(final RobocupAgent ra,
      final int numberOfNewHoles) {

    try {
      TeamSoccerScopeMediator.registerHole.invoke(
          TeamSoccerScopeMediator.registerClass, ra, new Integer(
              numberOfNewHoles));
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
    catch (final InvocationTargetException e) {
      e.printStackTrace();
    }
  }

  /**
   * This method is used to add heared messages to a scene of soccerscope. Used
   * by the agents.
   * 
   * @param pIndex -
   *          the index of the player agent.
   * @param buf -
   *          the communicated messages (via hear-command)
   */
  public static void soccerScopeAddHearMessageToScene(final int pIndex,
      final String buf) {

    try {
      TeamSoccerScopeMediator.soccerScopeAddHearMessageToScene
          .invoke(TeamSoccerScopeMediator.soccerScopeInstance, new Integer(
              pIndex), buf);
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
    catch (final InvocationTargetException e) {
      e.printStackTrace();
    }
  }

  /**
   * Method to add spoken messages to soccerscope. Used by the agents.
   * 
   * @param index -
   *          the index of the player agent
   * @param msg -
   *          the communicated message (via say-command).
   */
  public static void soccerScopeAddSayMessageToScene(final int index,
      final String msg) {

    if (TeamSoccerScopeMediator.soccerScopeInstance == null) {
      TeamSoccerScopeMediator.soccerScopeInstance = TeamSoccerScopeMediator
          .getSoccerScopeInstance();
    }
    try {
      TeamSoccerScopeMediator.soccerScopeAddSayMessageToScene.invoke(
          TeamSoccerScopeMediator.soccerScopeInstance, new Integer(index), msg);

    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
    catch (final InvocationTargetException e) {
      e.printStackTrace();
    }
  }

  /**
   * This method sets the default host of soccerscope, which is used to
   * automatically connect to the server.
   * 
   * @param host -
   *          the host-name of the server
   */
  public static void soccerServerConnectionDefaultHost(final String host) {

    try {
      final Class c = Class.forName("soccerscope.net.SoccerServerConnection");
      c.getField("DEFAULT_HOST").set(c, host);

      TeamSoccerScopeMediator.soccerScopeInstance = TeamSoccerScopeMediator
          .getSoccerScopeInstance();
      TeamSoccerScopeMediator.soccerScopeAddHearMessageToScene = TeamSoccerScopeMediator
          .getSoccerScopeAddHearMessageToScene();
      TeamSoccerScopeMediator.soccerScopeAddSayMessageToScene = TeamSoccerScopeMediator
          .getSoccerScopeAddSayMessageToScene();
    }
    catch (final ClassNotFoundException e) {
      e.printStackTrace();
    }
    catch (final SecurityException e) {
      e.printStackTrace();
    }
    catch (final NoSuchFieldException e) {
      e.printStackTrace();
    }
    catch (final IllegalArgumentException e) {
      e.printStackTrace();
    }
    catch (final IllegalAccessException e) {
      e.printStackTrace();
    }
  }
}
